########################################################################
#  Pyfm - personal finance data-base
#  Copyright (C) 2007,8 Ofer Barkai, Ehud Ben-Reuven
#  udi@benreuven.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation version 2.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#########################################################################
"""Library for handling variouse SECID related functions"""
from operator import *
import re

reticker=re.compile("\^?[A-Z][A-Z0-9]*\Z") # A ticker with a ^ at its start is an index
def validTicker(s):
    """Ticker used by US stock exchange"""
    return bool(reticker.match(s))

def validCurrency(s):
    """Check currency for ISO standard"""
    return len(s)==3 and s.isalpha() and s.isupper()

def validCusip(n):
    if not str(n): return False
    n=n.upper()
    if not len(n)==9: return False
    if not n.isalnum(): return False
    c=[ord(x)-ord('0') if x.isdigit() else ord(x)-ord('A')+10 for x in n]
    c[1::2]=[2*x for x in c[1::2]]
    lc=[mod(x,10) for x in c]
    hc=[div(x,10) for x in c]
    s=mod(sum(lc)+sum(hc),10)
    return s==0
    
def validIsin(isin, calculateControl=False):
    if not calculateControl and len(isin) != 12:
        return False
    digits = []
    for c in isin:
        if c >= '0' and c <= '9':
            d = ord(c) - ord('0')
        elif c >= 'A' and c <= 'Z':
            d = ord(c) - ord('A') + 10
        elif c >= 'a' and c <= 'z':
            d = ord(c) - ord('a') + 10
        else:
            return False
        if d >= 10:
            digits.append(d / 10)
        digits.append(d % 10)
    sum = 0
    if calculateControl:
        mult = 2
    else:
        mult = 1
    for d in reversed(digits):
        prod = mult * d
        mult = 3 - mult
        if prod >= 10:
            prod = prod / 10 + prod % 10
        sum += prod
    if calculateControl:
        return (10 - sum % 10) % 10
    return sum % 10 == 0

def makeIsin(countryCode, securityId, withControl=True):
    isin = countryCode + securityId
    if withControl:
        return isin + ("%d" % validIsin(isin, True))
    return isin
    
if __name__=='__main__':
    print "CUSIP"
    for cusip in ['123456782','345370860','594918104','594918104','X40262KT7','00254EBP6','008281AN7','X40262KT7','00254EBP6']:
        print cusip,validCusip(cusip),makeIsin("US", cusip),validIsin(makeIsin("US", cusip))
    print "ISIN"
    for isin in ['US008281AN71','XS0212544364','USX40262KT76','XS0201900270','US00254EBP60']:
        print isin,validIsin(isin)
        if isin.startswith('US'):
            cusip=isin[2:-1]
            print cusip,validCusip(cusip),makeIsin("US", cusip),validIsin(makeIsin("US", cusip))

